" Tests for 'listchars' display with 'list' and :list

source view_util.vim

func Test_listchars()
  enew!
  set ff=unix
  set list

  set listchars+=tab:>-,space:.,trail:<
  call append(0, [
	      \ '	aa	',
	      \ '  bb	  ',
	      \ '   cccc	 ',
	      \ 'dd        ee  	',
	      \ ' '
	      \ ])
  let expected = [
	      \ '>-------aa>-----$',
	      \ '..bb>---<<$',
	      \ '...cccc><$',
	      \ 'dd........ee<<>-$',
	      \ '<$'
	      \ ]
  redraw!
  for i in range(1, 5)
    call cursor(i, 1)
    call assert_equal([expected[i - 1]], ScreenLines(i, '$'->virtcol()))
  endfor

  set listchars-=trail:<
  let expected = [
	      \ '>-------aa>-----$',
	      \ '..bb>---..$',
	      \ '...cccc>.$',
	      \ 'dd........ee..>-$',
	      \ '.$'
	      \ ]
  redraw!
  for i in range(1, 5)
    call cursor(i, 1)
    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
  endfor

  " tab with 3rd character.
  set listchars-=tab:>-
  set listchars+=tab:<=>,trail:-
  let expected = [
	      \ '<======>aa<====>$',
	      \ '..bb<==>--$',
	      \ '...cccc>-$',
	      \ 'dd........ee--<>$',
	      \ '-$'
	      \ ]
  redraw!
  for i in range(1, 5)
    call cursor(i, 1)
    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
  endfor

  " tab with 3rd character and linebreak set
  set listchars-=tab:<=>
  set listchars+=tab:<·>
  set linebreak
  let expected = [
	      \ '<······>aa<····>$',
	      \ '..bb<··>--$',
	      \ '...cccc>-$',
	      \ 'dd........ee--<>$',
	      \ '-$'
	      \ ]
  redraw!
  for i in range(1, 5)
    call cursor(i, 1)
    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
  endfor
  set nolinebreak
  set listchars-=tab:<·>
  set listchars+=tab:<=>

  set listchars-=trail:-
  let expected = [
	      \ '<======>aa<====>$',
	      \ '..bb<==>..$',
	      \ '...cccc>.$',
	      \ 'dd........ee..<>$',
	      \ '.$'
	      \ ]
  redraw!
  for i in range(1, 5)
    call cursor(i, 1)
    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
  endfor

  set listchars-=tab:<=>
  set listchars+=tab:>-
  set listchars+=trail:<
  set nolist
  normal ggdG
  call append(0, [
	      \ '  fff	  ',
	      \ '	gg	',
	      \ '     h	',
	      \ 'iii    	  ',
	      \ ])
  let l = split(execute("%list"), "\n")
  call assert_equal([
	      \ '..fff>--<<$',
	      \ '>-------gg>-----$',
	      \ '.....h>-$',
	      \ 'iii<<<<><<$', '$'], l)

  " Test lead and trail
  normal ggdG
  set listchars&
  set listchars+=lead:>,trail:<,space:x
  set list

  call append(0, [
	      \ '    ffff    ',
	      \ '          gg',
	      \ 'h           ',
	      \ '            ',
	      \ '    0  0    ',
	      \ ])

  let expected = [
	      \ '>>>>ffff<<<<$',
	      \ '>>>>>>>>>>gg$',
	      \ 'h<<<<<<<<<<<$',
	      \ '<<<<<<<<<<<<$',
	      \ '>>>>0xx0<<<<$',
              \ '$'
	      \ ]
  redraw!
  for i in range(1, 5)
    call cursor(i, 1)
    call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
  endfor

  call assert_equal(expected, split(execute("%list"), "\n"))

  " test nbsp
  normal ggdG
  set listchars=nbsp:X,trail:Y
  set list
  " Non-breaking space
  let nbsp = nr2char(0xa0)
  call append(0, [ ">" .. nbsp .. "<" ])

  let expected = '>X< '

  redraw!
  call cursor(1, 1)
  call assert_equal([expected], ScreenLines(1, virtcol('$')))

  set listchars=nbsp:X
  redraw!
  call cursor(1, 1)
  call assert_equal([expected], ScreenLines(1, virtcol('$')))

  " test extends
  normal ggdG
  set listchars=extends:Z
  set nowrap
  set nolist
  call append(0, [ repeat('A', &columns + 1) ])

  let expected = repeat('A', &columns)

  redraw!
  call cursor(1, 1)
  call assert_equal([expected], ScreenLines(1, &columns))

  set list
  let expected = expected[:-2] . 'Z'
  redraw!
  call cursor(1, 1)
  call assert_equal([expected], ScreenLines(1, &columns))

  enew!
  set listchars& ff&
endfunc

" Test that unicode listchars characters get properly inserted
func Test_listchars_unicode()
  enew!
  let oldencoding=&encoding
  set encoding=utf-8
  set ff=unix

  set listchars=eol:⇔,space:␣,nbsp:≠,tab:←↔→
  set list

  let nbsp = nr2char(0xa0)
  call append(0, ["a\tb c" .. nbsp .. "d"])
  let expected = ['a←↔↔↔↔↔→b␣c≠d⇔']
  redraw!
  call cursor(1, 1)
  call assert_equal(expected, ScreenLines(1, virtcol('$')))
  let &encoding=oldencoding
  enew!
  set listchars& ff&
endfunction

" Tests that space characters following composing character won't get replaced
" by listchars.
func Test_listchars_composing()
  enew!
  let oldencoding=&encoding
  set encoding=utf-8
  set ff=unix
  set list

  set listchars=eol:$,space:_,nbsp:=
  
  let nbsp1 = nr2char(0xa0)
  let nbsp2 = nr2char(0x202f)
  call append(0, [
        \ "  \u3099\t \u309A" .. nbsp1 .. nbsp1 .. "\u0302" .. nbsp2 .. nbsp2 .. "\u0302",
        \ ])
  let expected = [
        \ "_ \u3099^I \u309A=" .. nbsp1 .. "\u0302=" .. nbsp2 .. "\u0302$"
        \ ]
  redraw!
  call cursor(1, 1)
  call assert_equal(expected, ScreenLines(1, virtcol('$')))
  let &encoding=oldencoding
  enew!
  set listchars& ff&
endfunction

" Check for the value of the 'listchars' option
func s:CheckListCharsValue(expected)
  call assert_equal(a:expected, &listchars)
  call assert_equal(a:expected, getwinvar(0, '&listchars'))
endfunc

" Test for using a window local value for 'listchars'
func Test_listchars_window_local()
  %bw!
  set list listchars&
  new
  " set a local value for 'listchars'
  setlocal listchars=tab:+-,eol:#
  call s:CheckListCharsValue('tab:+-,eol:#')
  " When local value is reset, global value should be used
  setlocal listchars=
  call s:CheckListCharsValue('eol:$')
  " Use 'setlocal <' to copy global value
  setlocal listchars=space:.,extends:>
  setlocal listchars<
  call s:CheckListCharsValue('eol:$')
  " Use 'set <' to copy global value
  setlocal listchars=space:.,extends:>
  set listchars<
  call s:CheckListCharsValue('eol:$')
  " Changing global setting should not change the local setting
  setlocal listchars=space:.,extends:>
  setglobal listchars=tab:+-,eol:#
  call s:CheckListCharsValue('space:.,extends:>')
  " when split opening a new window, local value should be copied
  split
  call s:CheckListCharsValue('space:.,extends:>')
  " clearing local value in one window should not change the other window
  set listchars&
  call s:CheckListCharsValue('eol:$')
  close
  call s:CheckListCharsValue('space:.,extends:>')

  " use different values for 'listchars' items in two different windows
  call setline(1, ["\t  one  two  "])
  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
  split
  setlocal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
  split
  set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  " changing the global setting should not change the local value
  setglobal listchars=tab:[.],lead:#,space:_,trail:.,eol:&
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  set listchars<
  call assert_equal(['[......]##one__two..&'], ScreenLines(1, virtcol('$')))

  " Using setglobal in a window with local setting should not affect the
  " window. But should impact other windows using the global setting.
  enew! | only
  call setline(1, ["\t  one  two  "])
  set listchars=tab:[.],lead:#,space:_,trail:.,eol:&
  split
  setlocal listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
  split
  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
  setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))

  " Setting the global setting to the default value should not impact a window
  " using a local setting
  split
  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
  setglobal listchars&vim
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))

  " Setting the local setting to the default value should not impact a window
  " using a global setting
  set listchars=tab:{.},lead:-,space:=,trail:#,eol:$
  split
  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  setlocal listchars&vim
  call assert_equal(['^I  one  two  $'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['{......}--one==two##$'], ScreenLines(1, virtcol('$')))

  " Using set in a window with a local setting should change it to use the
  " global setting and also impact other windows using the global setting
  split
  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  set listchars=tab:+-+,lead:^,space:>,trail:<,eol:%
  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))

  " Setting invalid value for a local setting should not impact the local and
  " global settings
  split
  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
  let cmd = 'setlocal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x'
  call assert_fails(cmd, 'E474:')
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))

  " Setting invalid value for a global setting should not impact the local and
  " global settings
  split
  setlocal listchars=tab:<->,lead:_,space:.,trail:@,eol:#
  let cmd = 'setglobal listchars=tab:{.},lead:-,space:=,trail:#,eol:$,x'
  call assert_fails(cmd, 'E474:')
  call assert_equal(['<------>__one..two@@#'], ScreenLines(1, virtcol('$')))
  close
  call assert_equal(['+------+^^one>>two<<%'], ScreenLines(1, virtcol('$')))

  %bw!
  set list& listchars&
endfunc

" vim: shiftwidth=2 sts=2 expandtab
